package asm.dispatch.test1;

import org.ricks.asm.Label;
import org.ricks.asm.MethodVisitor;
import org.ricks.asm.Opcodes;
import org.ricks.asm.Type;
import org.ricks.common.Tuple;
import org.ricks.ioc.App;
import org.ricks.ioc.AppContext;
import org.ricks.ioc.Bean;
import org.ricks.net.ActionMethod;
import org.ricks.ioc.bean.factory.AbstractBeanFactory;
import org.ricks.log.Logger;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.stream.Collectors;

import static org.ricks.asm.Opcodes.*;

/**
 * @author chenwei
 * @Description:构建函数也是方法
 * @date 2022/11/2915:36
 */
public class DispatchClinetVisitor extends MethodVisitor {

    static List<Tuple> tupleList = initPairs();

    protected DispatchClinetVisitor(MethodVisitor mv) {
        super(ASM7,mv);
    }

    @Override
    public void visitCode() {
        int line = 19;
        Set<String> classNames =  tupleList.stream().map(tuple -> (String)tuple.get(1)).collect(Collectors.toSet()); //对象入口
        for (String className : classNames) {
            String objName = className.substring(className.lastIndexOf(".") + 1,className.length()).toLowerCase(Locale.ROOT);
            String cName = className.replace(".","/");
            Label label0 = new Label();
            mv.visitLabel(label0);
            mv.visitLineNumber(line, label0);
            mv.visitLdcInsn(Type.getType("L"+cName+";"));
            mv.visitMethodInsn(INVOKESTATIC, "org/ricks/ioc/ApplicationContext", "getBean", "(Ljava/lang/Class;)Ljava/lang/Object;", false);
            mv.visitTypeInsn(CHECKCAST, cName);
            mv.visitFieldInsn(PUTSTATIC, "org/ricks/dispatch/Dispatcher", objName, "L"+cName+";");
            line += 1;
        }
    }

    @Override
    public void visitMaxs(int maxStack, int maxLocals) {
        super.visitMaxs(maxStack+1, maxLocals);
    }

    private static void  createMethodFunction(MethodVisitor methodVisitor, Tuple tuple, Label label, int lineNum, Label gotoLabel,boolean isFirst) {
        String className = tuple.get(1).toString();
        String cName = className.replace(".","/");
        String objName = className.substring(className.lastIndexOf(".") + 1,className.length()).toLowerCase(Locale.ROOT);
        methodVisitor.visitLabel(label);
        methodVisitor.visitLineNumber(lineNum, label);
        if(isFirst) methodVisitor.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.INTEGER}, 0, null); else methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
        methodVisitor.visitFieldInsn(GETSTATIC, "org/ricks/dispatch/Dispatcher", objName, "L"+cName+";");
        methodVisitor.visitVarInsn(ALOAD, 0);
        methodVisitor.visitMethodInsn(INVOKEVIRTUAL, cName, tuple.get(2), "(Lorg/demon/common/Context;)V", false);

//        methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "err", "Ljava/io/PrintStream;");
//        methodVisitor.visitLdcInsn("\u8fdb\u5165\u539f\u751f\u6001\u3002\u3002\u3002\u3002\u3002\u3002\u3002\u3002");
//        methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);

        methodVisitor.visitJumpInsn(GOTO, gotoLabel);

    }

    private static List<Tuple> initPairs() {

        App context = AppContext.getApplication();
        List<Tuple> pairs = new ArrayList<>();
        AbstractBeanFactory beanFactory = context.getBeanFactory();
        if(beanFactory != null) {
            List<Object> beans = beanFactory.getBeansForAnnotation(Bean.class);
            if (!beans.isEmpty()) {
                for (Object obj : beans) {
                    try {
                        Method[] methods = obj.getClass().getMethods();
                        for (Method method : methods) {
                            ActionMethod messageHandler = method.getAnnotation(ActionMethod.class);
                            if (messageHandler == null) {
                                continue;
                            }
                            Class<?>[] parameterClazzes = method.getParameterTypes();
                            if (parameterClazzes.length != 1) {
                                throw new IllegalArgumentException("消息处理方法的参数不正确，参数必须是一个。");
                            }
                            Class<?> commandClass = parameterClazzes[0];
                            if (!org.ricks.common.Context.class.isAssignableFrom(commandClass)) {
                                continue;
                            }
                            pairs.add(new Tuple(messageHandler.messageId(),getClassName(obj).replace("/","."),method.getName()));
                        }
                    } catch (Exception e) {
                        Logger.error("", e);
                    }
                }
            }
        }
        return pairs;
    }

    private static String getClassName(Object obj) {
        String beanName = obj.getClass().getName();
        return beanName.replace(".","/");
    }
}
